﻿using System;
using System.Diagnostics.Contracts;
using System.IO;

namespace OpenLS.Spreadsheet.Formats.Biff.Records
{
    internal class HyperlinkObject
    {
        private HyperlinkString _displayName;
        private byte[] _fileTime;
        private Guid _guid;
        private bool _hlstmfAbsFromGetdataRel;
        private bool _hlstmfHasCreationTime;
        //private bool _hlstmfHasDisplayName;
        private bool _hlstmfHasFrameName;
        private bool _hlstmfHasGuid;
       // private bool _hlstmfHasLocationStr;
        private bool _hlstmfHasMoniker;
        private bool _hlstmfIsAbsolute;
        private bool _hlstmfMonikerSavedAsStr;
        private bool _hlstmfSiteGaveDisplayName;
        private HyperlinkString _location;
        private HyperlinkString _moniker;
        private HyperlinkMoniker _oleMoniker;
        private uint _streamVersion = 2;
        private HyperlinkString _targetFrameName;

        public HyperlinkObject(Hyperlink hyperlink)
        {
            Contract.Requires(hyperlink != null);
            this._displayName = (HyperlinkString) hyperlink.Display;
            if (hyperlink.ExternalUri != null)
            {
                this._oleMoniker = new HyperlinkMoniker();
                _oleMoniker.Data = new UrlMoniker() {Url = hyperlink.ExternalUri.ToString()};
                this._hlstmfHasMoniker = true;
                this._hlstmfIsAbsolute = hyperlink.ExternalUri.IsAbsoluteUri;
                this._hlstmfSiteGaveDisplayName = true;
             //   this._hlstmfHasDisplayName = true;
                this._hlstmfMonikerSavedAsStr = false;
            }
            else if (hyperlink.Target != null)
            {

                this._location = (HyperlinkString) hyperlink.Target.FullLabel;
            }
            else
            {
                throw new NotImplementedException();
            }
        }

        public HyperlinkObject()
        {
        }

        public Hyperlink GetHyperlink (Cell source)
        {
            {
                Hyperlink result = new Hyperlink();
                if (this._oleMoniker != null)
                {
                    if (_oleMoniker.Data is UrlMoniker)
                    {
                        UrlMoniker urlMoniker = (UrlMoniker)_oleMoniker.Data;
                        result.ExternalUri = new Uri(urlMoniker.Url, _hlstmfIsAbsolute?UriKind.Absolute:UriKind.Relative);
                    }

                }
                else if (this._location != null)
                {
                    string loc = (string) _location;
                    var range = source.Worksheet.GetRangeReference(loc);
                    if (range != null)
                        result.Target = range.Range.TopLeftCell;
                }
                else
                {
                    throw new NotImplementedException();
                }
                return result;
            }
        }

        public void Read(BinaryReader reader)
        {
            _streamVersion = reader.ReadUInt32();
            uint line = reader.ReadUInt32();
            _hlstmfHasMoniker = (line & 0x01) != 0;
            _hlstmfIsAbsolute = (line & 0x02) != 0;
            _hlstmfSiteGaveDisplayName = (line & 0x04) != 0;
            bool _hlstmfHasLocationStr = (line & 0x08) != 0;
            bool _hlstmfHasDisplayName = (line & 0x010) != 0;
            _hlstmfHasGuid = (line & 0x020) != 0;
            _hlstmfHasCreationTime = (line & 0x040) != 0;
            _hlstmfHasFrameName = (line & 0x080) != 0;
            _hlstmfMonikerSavedAsStr = (line & 0x0100) != 0;
            _hlstmfAbsFromGetdataRel = (line & 0x0200) != 0;
            if (_hlstmfHasDisplayName)
            {
                _displayName = new HyperlinkString();
                _displayName.Read(reader);
            }
            if (_hlstmfHasFrameName)
            {
                _targetFrameName = new HyperlinkString();
                _targetFrameName.Read(reader);
            }
            if (_hlstmfHasMoniker)
            {
                if (_hlstmfMonikerSavedAsStr)
                {
                    _moniker = new HyperlinkString();
                    _moniker.Read(reader);
                }
                else
                {
                    _oleMoniker = new HyperlinkMoniker();
                    _oleMoniker.Read(reader);
                }
            }
            if (_hlstmfHasLocationStr)
            {
                _location = new HyperlinkString();
                _location.Read(reader);
            }
            if (_hlstmfHasGuid)
            {
                _guid = new Guid(reader.ReadBytes(16));
            }
            if (_hlstmfHasCreationTime)
            {
                _fileTime = reader.ReadBytes(8);
            }
        }

        public void Write(BinaryWriter writer)
        {
            writer.Write(_streamVersion);
            uint line = 0;
            if (_hlstmfHasMoniker) line |= 0x01;
            if (_hlstmfIsAbsolute) line |= 0x02;
            if (_hlstmfSiteGaveDisplayName) line |= 0x04;
            bool _hlstmfHasLocationStr = this._location != null;
            if (_hlstmfHasLocationStr) line |= 0x08;
            bool _hlstmfHasDisplayName = this._displayName != null;
            if (_hlstmfHasDisplayName) line |= 0x010;
            if (_hlstmfHasGuid) line |= 0x020;
            if (_hlstmfHasCreationTime) line |= 0x040;
            if (_hlstmfHasFrameName) line |= 0x080;
            if (_hlstmfMonikerSavedAsStr) line |= 0x0100;
            if (_hlstmfAbsFromGetdataRel) line |= 0x0200;
            writer.Write(line);
            if (_hlstmfHasDisplayName)
            {
                _displayName.Write(writer);
            }
            if (_hlstmfHasFrameName)
            {
                _targetFrameName.Write(writer);
            }
            if (_hlstmfHasMoniker)
            {
                if (_hlstmfMonikerSavedAsStr)
                    _moniker.Write(writer);
                else
                {
                    _oleMoniker.Write(writer);
                }
            }
            if (_hlstmfHasLocationStr)
            {
                _location.Write(writer);
            }
            if (_hlstmfHasGuid)
            {
                writer.Write(_guid.ToByteArray());
            }
            if (_hlstmfHasCreationTime)
            {
                writer.Write(_fileTime);
            }
        }
    }
}